/*
 * usb_utility.cpp
 *
 *  Created on: 14.02.2009
 *      Author: fregat
 */

# include "usb_utility.h"


using namespace fregat::microchip::usb;

void
usb_initialize ()
{
	usb_init ();
	usb_find_busses();
	usb_find_devices();
}

void
bus::readConfiguration ()
{
	device *pd = new device (*devices, this);
	pdc->add (pd);
	pd->readConfiguration();
	while (pd->next) {
		pd = new device (*pd->next, this);
		pd->readConfiguration();
		pdc->add (pd);
	}
}

bus::bus (usb_bus_t b) : usb_bus_t (b)
{
	pdc = new deviceCollector (this);
}

device::device (usb_device_t d, fregat::microchip::usb::bus *pb) : usb_device_t (d),
pcc (new configCollector ()), pec (new endpointCollector), pdh (0), pbus (pb)
{
}

config::config (usb_config_descriptor_t c, device *pd) : usb_config_descriptor_t (c), pdevice (pd)
{
	pic = new interfaceCollector ();
}

interface_::interface_ (usb_interface_descriptor uid, config *pc) :
	usb_interface_descriptor (uid), pconfig (pc)
{
	pec = new endpointCollector ();
}

endpoint::endpoint (usb_endpoint_descriptor ued, interface_ *pi) :
	usb_endpoint_descriptor (ued), pinterface (pi)
{
}

void
device::readConfiguration ()
{
	for (usb_config_descriptor *pcd = config; pcd != config + descriptor.bNumConfigurations; ++pcd) {
		fregat::microchip::usb::config *pc = new fregat::microchip::usb::config(*pcd, this);
		pc->readConfiguration(*pec);
		pcc->add (pc);
	}

}

void
config::readConfiguration (endpointCollector &pec)
{
	for (usb_interface *pi = interface;	pi != interface + bNumInterfaces; ++pi) {
		fregat::microchip::usb::interface_ *pid = new fregat::microchip::usb::interface_ (*(pi->altsetting), this);
		pid->readConfiguration (pec);
		pic->add (pid);
	}
}

void
interface_::readConfiguration (endpointCollector &pec_)
{
	for (usb_endpoint_descriptor *ped = endpoint; ped != endpoint + bNumEndpoints; ++ped) {
		fregat::microchip::usb::endpoint *pep;
		if (USB_ENDPOINT_TYPE_MASK & ped->bmAttributes == USB_ENDPOINT_TYPE_BULK)
			if (USB_ENDPOINT_DIR_MASK & ped->bEndpointAddress)
				pep = new fregat::microchip::usb::inpoint_bulk (*ped, this);
			else
				pep = new fregat::microchip::usb::outpoint_bulk (*ped, this);
		else
			pep = new fregat::microchip::usb::endpoint (*ped, this);
		pec->add (pep);
		pec_.add (pep);
	}
}

device *
bus::findDevice(vendor_t v, productid_t pid)
{
	std::list <device *>::iterator piter = std::find_if ( pdc->begin (), pdc->end (),
		boost::bind (&device::compare_vendor, _1, v, pid));
	if (piter == pdc->end())
		throw device_not_found (v,pid);
	return *piter;
}

inpoint_bulk *
device::findInBulk ()
{
	inpoint_bulk_search_visitor insv;
	for (std::list <endpoint *>::iterator piter = pec->begin ();
		piter != pec->end (); ++piter) {

		(*piter)->accept(&insv);
	}
	if (insv.size() == 0)
		return 0;
	return *insv.begin();
}

outpoint_bulk *
device::findOutBulk ()
{
	outpoint_bulk_search_visitor outsv;
	for (std::list <endpoint *>::iterator piter = pec->begin ();
		piter != pec->end (); ++piter) {

		(*piter)->accept(&outsv);
	}
	if (outsv.size() == 0)
		return 0;
	return *outsv.begin();
}


void
device::open ()
{
	pdh = usb_open(this);
	if (pdh == 0)
		throw device_not_opened (descriptor.idVendor, descriptor.idProduct);
}

void
device::write (const std::vector<unsigned char> &v, endpoint *ep, timeoutms_t to)
{
	if(usb_set_configuration(pdh, 1) < 0) {
		usb_close (pdh);
		throw config_invalid ();
	}

	int retval = usb_claim_interface (pdh, ep->getInterface()->bInterfaceNumber);
	if (retval < 0) {
		usb_close (pdh);
		throw claim_interface_invalid (retval);
	}

	retval = usb_bulk_write (pdh, ep->bEndpointAddress, (char*)&(*v.begin()), v.size(), to );

}

//void write (const std::vector<unsigned char> &v, endpoint *ep, timeoutms_t to);

void
device::read (std::vector <unsigned char> &v, endpoint *ep, timeoutms_t to)
{
	int retval = usb_claim_interface (pdh, ep->getInterface()->bInterfaceNumber);
	if (retval < 0) {
		usb_close (pdh);
		throw claim_interface_invalid (retval);
	}

	retval = usb_bulk_read (pdh, ep->bEndpointAddress, (char*)&(*v.begin()), v.size(), to );
	std::cerr << "retval = " << std::dec << retval << std::endl;

}


void
outpoint_bulk::write (const std::vector <unsigned char> &v, timeoutms_t to)
{
	device *pd = getInterface()->getConfig()->getDevice();
	pd->write (v, this, to);
}

void
inpoint_bulk::read (std::vector <unsigned char> &v, timeoutms_t to)
{
	device *pd = getInterface ()->getConfig ()->getDevice ();
	pd->read (v, this, to);
}


